package top.v5it.japi.plus.core.spring.web.interceptor;

import cn.hutool.core.date.StopWatch;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import top.v5it.japi.plus.common.context.AuthorizedContextHolder;
import top.v5it.japi.plus.core.api.ErrorResult;
import top.v5it.japi.plus.core.api.ResultStatus;
import top.v5it.japi.plus.core.exception.JapiAuthorizedException;
import top.v5it.japi.plus.core.exception.JapiSignatureException;
import top.v5it.japi.plus.core.util.HttpServletUtil;
import top.v5it.japi.plus.log.OperLogService;
import top.v5it.japi.plus.log.OperLogUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.util.Map;
import java.util.Optional;

import static top.v5it.japi.plus.common.ConstantCommon.LOG_DIVIDING_LINE;


/**
 * 自定义拦截器
 *
 * @author zhanpu
 * @date 20-11-24
 */
@Slf4j
@RequiredArgsConstructor
public abstract class AbstractJapiInterceptor implements HandlerInterceptor {

    private final OperLogService operLogService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 开始时间
        long startTime = Instant.now().toEpochMilli();

        StopWatch stopWatch = new StopWatch("请求认证", false);
        stopWatch.start();

        Optional<ErrorResult> optionalErrorResult = Optional.empty();
        try {
            preHandle(request, response, startTime);
        } catch (JapiAuthorizedException | JapiSignatureException e) {
            optionalErrorResult = Optional.of(ErrorResult.failure(e.getResultStatusTemplate(), e.getMessage()));
        } catch (IllegalArgumentException e) {
            optionalErrorResult = Optional.of(ErrorResult.failure(ResultStatus.PARAM_ERROR, e.getMessage()));
        } catch (Exception e) {
            // 优化不抛出敏感信息到ui
            log.error(e.getMessage());
            optionalErrorResult = Optional.of(ErrorResult.failure(ResultStatus.UNKNOWN_ERROR));
        }

        stopWatch.stop();

        if (log.isInfoEnabled() && stopWatch.getTotalTimeMillis() > 500L) {
            log.info(stopWatch.toString());
        }

        return errorHandle(request, response, optionalErrorResult, stopWatch.getTotalTimeMillis());
    }

    /**
     * 清理资源
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        // 优化当未授权或者授权失败还没有设置AuthorizedContextHolder的空指针异常
        Optional.ofNullable(AuthorizedContextHolder.getRequestAuthAttributes())
                .ifPresent(v -> {
                    long time = Instant.now().toEpochMilli() - v.getStartTime();

                    // 请求用时大于3秒，打印用时日志
                    if (time > 3000L) {
                        log.warn("{}请求用时\t\t单位\n{}\t\tmillis{}", LOG_DIVIDING_LINE, time, LOG_DIVIDING_LINE);
                    }

                    log.debug("清除资源");

                    AuthorizedContextHolder.resetRequestAuthAttributes();
                });

    }

    /**
     * 自定义处理方法
     *
     * @param request   {@link HttpServletRequest}
     * @param response  {@link HttpServletResponse}
     * @param startTime 时间
     * @throws Exception 异常信息
     */
    protected abstract void preHandle(HttpServletRequest request, HttpServletResponse response, final long startTime) throws Exception;

    /**
     * 自定义后置处理方法
     *
     * @param request   {@link HttpServletRequest}
     * @param response  {@link HttpServletResponse}
     * @param authMap   授权信息
     * @param startTime 时间
     * @throws Exception 异常信息
     */
    protected void prePostHandle(HttpServletRequest request, HttpServletResponse response, Map<String, String> authMap, final long startTime) throws Exception {

    }

    /**
     * 异常处理
     *
     * @param request
     * @param response
     * @param optionalErrorResult
     * @param executeTime
     * @return
     */
    private boolean errorHandle(HttpServletRequest request, HttpServletResponse response, Optional<ErrorResult> optionalErrorResult, final long executeTime) {
        if (optionalErrorResult.isPresent()) {
            //保存日志
            ErrorResult errorResult = optionalErrorResult.get();

            operLogService.addOperLog(OperLogUtil.builder(request, executeTime, errorResult.getStatus(), errorResult.getMessage()));
            HttpServletUtil.write(response, errorResult);
            return false;
        }

        return true;
    }

}
